#include "unity.h"
#include "double_list.h"

enum status {
        STATUS_OK,
        STATUS_ERROR
};

/* Define a structure with four members */
struct info {
        int value;
        enum status stat;
        char flag;
        char name[20];
};

/* Define a structure containing a list node */
struct my_data {
        struct info info;
        struct list_head list;
};

struct my_data item1, item2, item3;
struct list_head my_list;

/**
 *  Initialize the list and data item
 */
void setUp(void) {
        INIT_LIST_HEAD(&my_list);

        item1 = (struct my_data) {
                .info = {
                        .value = 1,
                        .stat = STATUS_OK,
                        .flag = 'A',
                        .name = "Item1"
                },
                .list = LIST_HEAD_INIT(item1.list)
        };

        item2 = (struct my_data) {
                .info = {
                        .value = 2,
                        .stat = STATUS_ERROR,
                        .flag = 'B',
                        .name = "Item2"
                },
                .list = LIST_HEAD_INIT(item2.list)
        };

        item3 = (struct my_data) {
                .info = {
                        .value = 3,
                        .stat = STATUS_OK,
                        .flag = 'C',
                        .name = "Item3"
                },
                .list = LIST_HEAD_INIT(item3.list)
        };
}

/**
 *  Initialize the list and data item
 */
void tearDown(void)
{
        struct list_head *pos, *n;
        list_for_each_safe(pos, n, &my_list) {
                struct my_data *entry = list_entry(pos, struct my_data, list);
                list_del(&entry->list);
        }
}

/**
 * Initialize the list and data item
 */
void test_list_initialization(void)
{
        TEST_ASSERT_TRUE_MESSAGE(list_empty(&my_list),
                                 "List should be empty after initialization.");
}

/**
 *  Initialize the list and data item
 */
void test_list_add(void)
{
        list_add(&item1.list, &my_list);
        TEST_ASSERT_FALSE_MESSAGE(list_empty(&my_list),
                                  "List should not be empty"
                                  "after adding an item.");

        TEST_ASSERT_EQUAL_PTR_MESSAGE(&item1.list,
                                      my_list.next,
                                      "The first item should be"
                                      "the head of the list.");
}

/**
 *  Initialize the list and data item
 */
void test_list_del(void) {
    /**
     *  Test deleting an item from the list
     */
    list_add(&item1.list, &my_list);
    list_del(&item1.list);
    TEST_ASSERT_TRUE_MESSAGE(
        list_empty(&my_list),
        "List should be empty after deleting the item."
    );
}

/**
 *  Initialize the list and data item
 */
void test_list_for_each_safe(void)
{
        list_add(&item1.list, &my_list);
        list_add(&item2.list, &my_list);
        list_add(&item3.list, &my_list);

        struct list_head *pos, *n;
        struct my_data *entry;
        int expected_values[] = {3, 2, 1};
        enum status expected_status[] = {STATUS_OK, STATUS_ERROR, STATUS_OK};
        char expected_flags[] = {'C', 'B', 'A'};
        char *expected_names[] = {"Item3", "Item2", "Item1"};

        int i = 0;

        list_for_each_safe(pos, n, &my_list) {
                entry = list_entry(pos, struct my_data, list);
                TEST_ASSERT_EQUAL_INT_MESSAGE(expected_values[i],
                                              entry->info.value,
                                              "Value mismatch.");

                TEST_ASSERT_EQUAL_INT_MESSAGE(expected_status[i],
                                              entry->info.stat,
                                              "Status mismatch.");

                TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected_flags[i],
                                               entry->info.flag,
                                               "Flag mismatch.");

                TEST_ASSERT_EQUAL_STRING_MESSAGE(expected_names[i],
                                                 entry->info.name,
                                                 "Name mismatch.");

                list_del(&entry->list);

                if (i == 0) {
                        TEST_ASSERT_FALSE_MESSAGE(list_empty(&my_list),
                                              "List should not be empty after "
                                              "deleting one item.");

                        TEST_ASSERT_EQUAL_PTR_MESSAGE(&item2.list,
                                                  my_list.next,
                                                  "The first item should"
                                                  " now be item2.");
                }

                i++;
        }

        TEST_ASSERT_TRUE_MESSAGE(list_empty(&my_list),
                                 "List should be empty after"
                                 " all items are deleted.");
}

/**
 *  Initialize the list and data item
 */
void test_list_for_each_entry_safe(void)
{
        list_add(&item1.list, &my_list);
        list_add(&item2.list, &my_list);
        list_add(&item3.list, &my_list);

        struct my_data *entry, *next;
        int expected_values[] = {3, 2, 1};
        enum status expected_status[] = {STATUS_OK, STATUS_ERROR, STATUS_OK};
        char expected_flags[] = {'C', 'B', 'A'};
        char *expected_names[] = {"Item3", "Item2", "Item1"};

        int i = 0;

        list_for_each_entry_safe(entry, next, &my_list, list) {
                TEST_ASSERT_EQUAL_INT_MESSAGE(expected_values[i],
                                              entry->info.value,
                                              "Value mismatch.");

                TEST_ASSERT_EQUAL_INT_MESSAGE(expected_status[i],
                                              entry->info.stat,
                                              "Status mismatch.");

                TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected_flags[i],
                                               entry->info.flag,
                                               "Flag mismatch.");

                TEST_ASSERT_EQUAL_STRING_MESSAGE(expected_names[i],
                                                 entry->info.name,
                                                 "Name mismatch.");

                list_del(&entry->list);
                break;
                i++;
        }

        /* Verify the state of the list */
        struct my_data *remaining_entry;
        int remaining_expected_values[] = {2, 1};
        enum status remaining_expected_status[] = {STATUS_ERROR, STATUS_OK};
        char remaining_expected_flags[] = {'B', 'A'};
        char *remaining_expected_names[] = {"Item2", "Item1"};

        i = 0;
        list_for_each_entry(remaining_entry, &my_list, list) {
                TEST_ASSERT_EQUAL_INT_MESSAGE(remaining_expected_values[i],
                                              remaining_entry->info.value,
                                              "Value mismatch after deletion.");
                TEST_ASSERT_EQUAL_INT_MESSAGE(remaining_expected_status[i],
                                              remaining_entry->info.stat,
                                              "Status mismatch after deletion.");
                TEST_ASSERT_EQUAL_CHAR_MESSAGE(remaining_expected_flags[i],
                                               remaining_entry->info.flag,
                                               "Flag mismatch after deletion.");
                TEST_ASSERT_EQUAL_STRING_MESSAGE(remaining_expected_names[i],
                                                 remaining_entry->info.name,
                                                 "Name mismatch after deletion.");
                i++;
        }
}

/**
 *  Initialize the list and data item
 */
int main(void)
{
        UNITY_BEGIN();

        RUN_TEST(test_list_initialization);
        RUN_TEST(test_list_add);
        RUN_TEST(test_list_for_each_safe);
        RUN_TEST(test_list_del);
        RUN_TEST(test_list_for_each_entry_safe);

        return UNITY_END();
}
